package com.gmrz.webauthn.protocol.v1.schema;

import com.gmrz.util.Convert;
import com.gmrz.webauthn.common.CoseConstants;
import com.gmrz.webauthn.protocol.v1.processor.WebAuthnCborException;
import com.gmrz.webauthn.protocol.v1.processor.WebAuthnErrorCode;
import com.gmrz.webauthn.protocol.v1.processor.WebAuthnException;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.math.BigInteger;
import java.nio.ByteBuffer;
import java.util.Map;


public class AttestedCredentialData {
    private static final Logger LOG = LogManager.getLogger(AuthenticatorData.class);
    private static final String COSE_ALG_KEY_INDEX = "3";
    private AAGUID aaguid;
    private short length;
    private String credentialID;
    private JsonWebKey credentialPublicKey;
    private byte[] cborPublicKey;

    public AAGUID getAaguid() {
        return this.aaguid;
    }

    public void setAaguid(AAGUID aaguid) {
        this.aaguid = aaguid;
    }


    public void parseCosePublicKey(Map<String, Object> pubKeyMap)
            throws WebAuthnCborException, WebAuthnException {
        JsonWebKey jsonWebKey = new JsonWebKey();


        SignAlgorithmIdentifier signAlgoID = SignAlgorithmIdentifier.forCoseAlgID(Long.valueOf(pubKeyMap.get("3").toString()));
        LOG.debug(" The pubkey algo is : {}", signAlgoID);
        jsonWebKey.setAlg(signAlgoID);

        switch (signAlgoID) {
            case ES256:
            case ES384:
            case ES512:
                jsonWebKey.setX(new BigInteger((byte[]) pubKeyMap.get(String.valueOf(CoseConstants.ECDSA_X_COMPONENT))));
                jsonWebKey.setY(new BigInteger((byte[]) pubKeyMap.get(String.valueOf(CoseConstants.ECDSA_Y_COMPONENT))));
                break;

            case RS256:
            case RS384:
            case RS512:
            case PS256:
            case PS384:
            case PS512:
            case RS1:
                jsonWebKey.setN(new BigInteger((byte[]) pubKeyMap.get(String.valueOf(CoseConstants.RSA_N_COMPONENT))));
                jsonWebKey.setE(new BigInteger((byte[]) pubKeyMap.get(String.valueOf(CoseConstants.RSA_E_COMPONENT))));
                break;

            default:
                throw new WebAuthnException(WebAuthnErrorCode.ERROR_ALGORITHM_NOT_FOUND, " unsupported public key algo");
        }


        this.credentialPublicKey = jsonWebKey;
    }

    public String getCredentialID() {
        return this.credentialID;
    }

    public void setCredentialID(String credentialID) {
        this.credentialID = credentialID;
    }

    public short getLength() {
        return this.length;
    }

    public void setLength(short length) {
        this.length = length;
    }

    public JsonWebKey getCredentialPublicKey() {
        return this.credentialPublicKey;
    }

    public void setCredentialPublicKey(JsonWebKey credentialPublicKey) {
        this.credentialPublicKey = credentialPublicKey;
    }

    public byte[] getCborPublicKey() {
        return this.cborPublicKey;
    }

    public void setCborPublicKey(byte[] cborPublicKey) {
        this.cborPublicKey = cborPublicKey;
    }

    public byte[] encodeToBytes() throws WebAuthnException {
        if (this.aaguid == null) {
            String errorMsg = "Cannot encode Attestation data with AAGUID null";
            LOG.error(errorMsg);
            throw new WebAuthnException(WebAuthnErrorCode.STATUS_INVALID_ATTESTATION_DATA, errorMsg);
        }

        if (this.credentialID == null) {
            String errorMsg = "Cannot encode Attestation data with credentialID null";
            LOG.error(errorMsg);
            throw new WebAuthnException(WebAuthnErrorCode.STATUS_INVALID_ATTESTATION_DATA, errorMsg);
        }

        if (this.length == 0) {
            this.length = ((short) this.credentialID.length());
        }

        if (this.cborPublicKey == null) {
            String errorMsg = "Cannot encode Attestation data with CBOR encoded credential public key null";
            LOG.error(errorMsg);
            throw new WebAuthnException(WebAuthnErrorCode.STATUS_INVALID_ATTESTATION_DATA, errorMsg);
        }


        byte[] attestationData = new byte[18 + this.length + this.cborPublicKey.length];

        ByteBuffer.wrap(attestationData).put(this.aaguid.getBytes()).putShort(this.length)
                .put(Convert.fromBase64(this.credentialID)).put(this.cborPublicKey);

        return attestationData;
    }
}
